/**
 * 
 */
package ai;



import org.junit.Before
import org.junit.Test
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertFalse
import static org.junit.Assert.assertTrue

import java.util.Set

import org.junit.Before
import org.junit.Test

import ai.human.PlayerGroup
import ai.human.PlayerGroupsManager
import data.Player
import data.Position
import data.Data.TeamID
import engine.Pitch



/**
 * AI human test.
 * 
 * @author Tomasz Dąbrowski
 *
 */
class AIEngineHumanTest {

	AIEngineHuman aiEngineHuman
	GameEngineFake gameEngine

	/**
	 *
	 * @throws java.lang.Exception
	 *
	 * @author Tomasz Dąbrowski
	 */
	@Before
	public void setUp() throws Exception {
		gameEngine = new GameEngineFake()
		aiEngineHuman = new AIEngineHuman(gameEngine.teamA(), gameEngine)
	}

	/**
	 * Test method for {@link ai.AIEngineHuman#extractPath(java.util.List)}.
	 */
	@Test
	public void testExtractPath() {
		List path = [
			[0, 1],
			[0, 2],
			[0, 3],
			[0, 4],
			[1, 2],
			[2, 2],
			[3, 2],
			[4, 2],
			[5, 2]
		].collect {
			new Position(it[0], it[1])
		}
		aiEngineHuman.extractPath(path)
		List correctPath = [
			[0, 1],
			[1, 2],
			[2, 2],
			[3, 2],
			[4, 2],
			[5, 2]
		].collect {
			new Position(it[0], it[1])
		}
		assertEquals(correctPath, path)
	}

	/**
	 * Test method for {@link ai.AIEngineHuman#getPath(data.Position, data.Position)}.
	 */
	@Test
	public void testGetPath() {
		gameEngine.initPlayingField()
		aiEngineHuman.updateDangerValues()

		Position src = new Position(0, 5)
		Position dst = new Position(10, 5)
		List path = aiEngineHuman.getPath(src, dst, true, 0)
		assertEquals(10, path.size())

		src = new Position(0, 0)
		dst = new Position(19, 7)
		path = aiEngineHuman.getPath(src, dst, true, 0)
		assertEquals(19, path.size())

		List<Player> ps = gameEngine.teamB().players()[1..3]
		gameEngine.playingField.placePlayer(TeamID.TEAM_B, ps[0], new Position(3, 3))
		gameEngine.playingField.placePlayer(TeamID.TEAM_B, ps[1], new Position(10, 4))

		aiEngineHuman.updateDangerValues()
		path = aiEngineHuman.getPath(src, dst, true, 0)
		assertEquals(21, path.size())

		gameEngine.playingField.placePlayer(TeamID.TEAM_B, ps[2], new Position(17, 6))
		aiEngineHuman.updateDangerValues()
		path = aiEngineHuman.getPath(src, dst, true, 0)
		assertEquals(24, path.size())
	}

	/**
	 * Test method for {@link ai.AIEngineHuman#assignLeaderPosition(data.Player)}.
	 */
	@Test
	public void testAssignLeaderPosition() {
		gameEngine.initPlayingField()
		aiEngineHuman.updateDangerValues()

		Player p = gameEngine.teamA().players()[0]
		Position ballPos = new Position(0, 10)
		gameEngine.playingField.placePlayer(TeamID.TEAM_A, p, new Position(10, 10))
		gameEngine.playingField.placeBall(ballPos)

		Position dst = aiEngineHuman.assignLeaderPosition(p)
		assertEquals(p.MA(), p.distance(dst))

		gameEngine.initPlayingField()
		aiEngineHuman.updateDangerValues()
		gameEngine.playingField.placePlayer(TeamID.TEAM_A, p, new Position(5, 10))
		gameEngine.playingField.placeBall(ballPos)
		dst = aiEngineHuman.assignLeaderPosition(p)
		assertEquals(ballPos, dst)

		gameEngine.initPlayingField()
		aiEngineHuman.updateDangerValues()
		gameEngine.playingField.placePlayer(TeamID.TEAM_A, p, new Position(10, 10))
		p.setHasBall(true)
		dst = aiEngineHuman.assignLeaderPosition(p)
		assertEquals(p.MA(), p.distance(dst))

		gameEngine.initPlayingField()
		aiEngineHuman.updateDangerValues()
		gameEngine.playingField.placePlayer(TeamID.TEAM_A, p, new Position(20, 7))
		p.setHasBall(true)
		dst = aiEngineHuman.assignLeaderPosition(p)
		assertTrue(dst in (0..<Pitch.PITCH_HEIGHT).collect {
			new Position(Pitch.PITCH_WIDTH - 1, it)
		} )
	}

	/**
	 * Test method for {@link ai.AIEngineHuman#generatePlaymakersPositions(data.Position, java.util.List)}.
	 */
	@Test
	public void testGeneratePlaymakersPositions() {
		gameEngine.initPlayingField()
		aiEngineHuman.updateDangerValues()
		List pgs = PlayerGroupsManager.createGroups(gameEngine.teamA().players())
		Position pos = new Position(10, 10)
		Player p = gameEngine.teamA().players()[0]
		gameEngine.playingField.placePlayer(TeamID.TEAM_A, p, pos)
		List poss = aiEngineHuman.generatePlaymakersPositions(pos, pgs.collect { it.mainPlayer } )
		assertEquals(105, poss.size())

		Player pEnemy = gameEngine.teamB().players()[0]
		gameEngine.playingField.placePlayer(TeamID.TEAM_B, pEnemy, new Position(15, 3))
		aiEngineHuman.updateDangerValues()
		poss = aiEngineHuman.generatePlaymakersPositions(pos, pgs.collect { it.mainPlayer } )
		assertEquals(189, poss.size())
	}

	/**
	 * Test method for {@link ai.AIEngineHuman#kmeansClustering(java.util.List)}.
	 */
	@Test
	public void testKmeansClustering() {
		List groups = [
			[
				[1, 1],
				[10, 10],
				[2, 1],
				[4, 6]
			],
			[
				[1, 2],
				[11, 10],
				[8, 10],
				[5, 5]
			],
			[
				[1, 3],
				[9, 10],
				[3, 1],
				[5, 4]]
		].collect {
			it.collect {
				new Position(it[0], it[1])
			}
		}

		AIEngineHuman.kmeansClustering(groups)

		groups.each  {
			System.out.println(it);
		}

		List groupsList = [
			groups[0],
			groups[1],
			groups[2]
		]
		groupsList.remove(groupsList.findIndexOf {
			new Position(1, 1) in it
		})
		groupsList.remove(groupsList.findIndexOf {
			new Position(4, 6) in it
		}
		)
		groupsList.remove(groupsList.findIndexOf {
			new Position(9, 10) in it
		})


		assertEquals(0, groupsList.size())
	}


	/**
	 * Test method for {@link ai.AIEngineHuman#assingPlaymakersPositions(java.util.List, java.util.List)}.
	 */
	@Test
	public void testAssingPlaymakersPositions() {
		gameEngine.initPlayingField()

		List pgs = PlayerGroupsManager.createGroups(gameEngine.teamA().players())
		Position leaderPos = new Position(10, 10)
		Position pos1 = new Position(13, 8)
		Position pos2 = new Position(4, 11)
		Position pos3 = new Position(7, 6)
		Player leader = gameEngine.teamA().players()[0]
		Player p1 = gameEngine.teamA().players()[0]
		Player p2 = gameEngine.teamA().players()[1]
		Player p3 = gameEngine.teamA().players()[2]
		gameEngine.playingField.placePlayer(TeamID.TEAM_A, leader, leaderPos)
		gameEngine.playingField.placePlayer(TeamID.TEAM_A, p1, pos1)
		gameEngine.playingField.placePlayer(TeamID.TEAM_A, p2, pos2)
		gameEngine.playingField.placePlayer(TeamID.TEAM_A, p3, pos3)

		Player pEnemy = gameEngine.teamB().players()[0]
		gameEngine.playingField.placePlayer(TeamID.TEAM_B, pEnemy, new Position(2, 2))
		pEnemy = gameEngine.teamB().players()[1]
		gameEngine.playingField.placePlayer(TeamID.TEAM_B, pEnemy, new Position(2, 6))
		pEnemy = gameEngine.teamB().players()[2]
		gameEngine.playingField.placePlayer(TeamID.TEAM_B, pEnemy, new Position(11, 3))
		aiEngineHuman.updateDangerValues()

		def poss = aiEngineHuman.generatePlaymakersPositions(leaderPos, pgs.collect { it.mainPlayer } )
		poss = aiEngineHuman.assingPlaymakersPositions(new Position(5, 5), [p1, p2, p3], poss)

		assertEquals(poss.values().size(), (poss.values() as Set).size())
	}

	/**
	 * Test method for {@link ai.AIEngineHuman#generateSupportPositions(data.Player, data.Position)}.
	 */
	@Test
	public void testGenerateSupportPositions() {
		gameEngine.initPlayingField()

		List<PlayerGroup> pgs = PlayerGroupsManager.createGroups(gameEngine.teamA().players())
		Position mainPlayerPos = new Position(10, 10)
		Player mainPlayer = pgs[0].mainPlayer
		gameEngine.playingField.placePlayer(TeamID.TEAM_A, mainPlayer, mainPlayerPos)

		Player pEnemy = gameEngine.teamB().players()[0]

		Position enemyPos = new Position(5, 5)
		gameEngine.playingField.placePlayer(TeamID.TEAM_B, pEnemy, enemyPos)
		aiEngineHuman.updateDangerValues()

		Map poss = aiEngineHuman.generateSupportPositions(pgs[0].mainPlayer, mainPlayerPos)
		assertTrue(poss["T"].flatten().disjoint(poss["D"].flatten()))
		assertEquals(AIEngine.posRange(enemyPos) as Set, poss["T"][0] as Set)
		assertEquals(2, enemyPos.distance(poss["D"].find()))
	}

	/**
	 * Test method for {@link ai.AIEngineHuman#assignSupportPositions(java.util.List, java.util.List)}.
	 */
	@Test
	public void testAssignSupportPositions() {
		gameEngine.initPlayingField()

		List<PlayerGroup> pgs = PlayerGroupsManager.createGroups(gameEngine.teamA().players())
		Position mainPlayerPos = new Position(10, 10)
		Player mainPlayer = pgs[0].mainPlayer
		gameEngine.playingField.placePlayer(TeamID.TEAM_A, mainPlayer, mainPlayerPos)

		gameEngine.playingField.placePlayer(TeamID.TEAM_A, pgs[0].supportPlayers[0], new Position(7, 7))
		gameEngine.playingField.placePlayer(TeamID.TEAM_A, pgs[0].supportPlayers[1], new Position(12, 11))

		Player pEnemy = gameEngine.teamB().players()[0]

		Position enemyPos = new Position(5, 5)
		gameEngine.playingField.placePlayer(TeamID.TEAM_B, pEnemy, enemyPos)
		aiEngineHuman.updateDangerValues()

		Map poss = aiEngineHuman.generateSupportPositions(pgs[0].mainPlayer, mainPlayerPos)
		Map assign = aiEngineHuman.assignSupportPositions(pgs[0].supportPlayers, poss)
		assertTrue(poss["T"].flatten().disjoint(poss["D"].flatten()))
		assertTrue(assign[pgs[0].supportPlayers[0]] in poss["T"][0] )
		assertTrue(assign[pgs[0].supportPlayers[1]] in poss["D"].find())
	}
}
